package com.ruoyi.project.drug.service.impl;

import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.page.PageDomain;
import com.ruoyi.framework.web.page.TableSupport;
import com.ruoyi.project.drug.domain.Drug;
import com.ruoyi.project.drug.domain.DrugOrder;
import com.ruoyi.project.drug.domain.DrugOrderDetail;
import com.ruoyi.project.drug.domain.DrugsOrderAndOthers;
import com.ruoyi.project.drug.mapper.DrugMapper;
import com.ruoyi.project.drug.mapper.DrugsOrderAndOthersMapper;
import com.ruoyi.project.drug.redis.RedisDao;
import com.ruoyi.project.drug.service.IDrugsOrderAndOthersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 采购入库单管理 服务实现类型
 */
@Service
public class DrugsOrderAndOthersServiceImpl implements IDrugsOrderAndOthersService {
    @Autowired
    private DrugsOrderAndOthersMapper orderAndOthersMapper;
    @Autowired  // 注入Redis数据访问对象。
    private RedisDao redisDao;
    @Autowired
    private DrugMapper drugMapper;

    /**
     * 清空Redis中所有和 ruoyi:drugs:order: 相关的键值对。
     * 先根据 key的部分数据，查询相关的key
     * 删除查询到的所有key。
     */
    private void clearRedisCache(){
        // 查询key的集合
        Set<String> keys = redisDao.keys("ruoyi:drugs:order:*");
        System.out.println(keys);
        for(String key : keys) {
            redisDao.del(key);
        }
    }

    /**
     * 批量提交入库。更新对应的采购单据明细状态为 已入库（4）
     * 增加新的处理逻辑。
     * 在入库的时候，需要把采购的药品的数量，累加到药品信息表格的库存量中。
     *
     * 1. 根据采购单据明细主键，查询采购单据数据。
     * 2. 根据采购单据明细中的药品主键，查询药品数据。
     * 3. 累加查询到的药品库存量和采购单中的采购数量，并保存到药品信息表格中。
     *
     * 在更新了采购单信息后，必须删除redis中的所有缓存。包括提交审核功能、作废功能、审核通过、审核不通过等功能
     *
     * @param detailIds
     * @param detailOperator 入库操作人名字， 登录用户的名字
     * @return
     */
    @Override
    public int submitStorage(Integer[] detailIds, String detailOperator) {
        int rows = 0;
        Date now = new Date();
        for(Integer detailId : detailIds){
            // 根据采购单据明细主键，查询采购单据数据。
            DrugOrderDetail orderDetail = orderAndOthersMapper.selectByDetailId(detailId);
            // 根据采购单明细中的药品主键，查询药品
            Drug drug = drugMapper.selectById(orderDetail.getDetailDrugid());
            // 累加药品剩余库存和采购量
            drug.setDurgNumber(drug.getDurgNumber() + orderDetail.getDetailDrugNum().intValue());
            // 设置药品的修改时间和修改人。修改人就是当前登录用户
            drug.setUpdateBy(SecurityUtils.getUsername());
            drug.setUpdateTime(now);
            // 修改药品，更新药品的库存量、修改人、修改时间。
            drugMapper.updateById(drug);
            // 修改状态
            orderDetail.setDetailStatus(4);
            // 入库操作人
            orderDetail.setDetailOperator(detailOperator);
            // 入库时间，就是系统当前时间。
            orderDetail.setDetailDate(now);
            // 更新采购单明细
            rows += orderAndOthersMapper.updateDrugOrderDetail(orderDetail);
        }

        clearRedisCache();
        return rows;
    }

    /**
     * 批量提交作废。更新对应的采购单据明细状态为 作废（5）
     * @param detailIds
     * @return
     */
    @Override
    public int submitCancel(Integer[] detailIds) {
        int rows = 0;
        for(Integer detailId : detailIds){
            // 创建要修改状态的采购单据明细对象
            DrugOrderDetail orderDetail = new DrugOrderDetail();
            orderDetail.setDetailId(detailId);
            orderDetail.setDetailStatus(5);
            // 修改数据
            rows += orderAndOthersMapper.updateDrugOrderDetail(orderDetail);
        }
        clearRedisCache();
        return rows;
    }

    /**
     * 批量提交审核。更新对应的采购单据明细状态为 待审核（1）
     * @param detailIds
     * @return
     */
    @Override
    public int submitExamine(Integer[] detailIds) {
        int rows = 0;
        for (Integer detailId : detailIds){
            DrugOrderDetail orderDetail = new DrugOrderDetail();
            orderDetail.setDetailId(detailId);
            orderDetail.setDetailStatus(1);
            // 更新
            rows += orderAndOthersMapper.updateDrugOrderDetail(orderDetail);
        }
        clearRedisCache();
        return rows;
    }

    /**
     * 新增采购单据
     * @param supportId 供应商主键
     * @param createBy 登录用户名字
     * @param orderDetails 采购单据明细集合
     * @return
     */
    @Override
    public int insertDrugOrderAndDetails(Integer supportId, Double orderTotal, String createBy, List<DrugOrderDetail> orderDetails) {
        // 创建采购单据对象
        DrugOrder order = new DrugOrder();
        order.setOrderMan(createBy);
        order.setOrderSupId(supportId);
        order.setOrderTotal(orderTotal);
        order.setCreateBy(createBy);

        // 新增采购单据
        orderAndOthersMapper.insertDrugOrder(order);

        // 循环新增采购单据明细
        for(DrugOrderDetail drugOrderDetail : orderDetails){
            // 把订单主键设置到明细的外键字段中
            drugOrderDetail.setOrderId(order.getOrderId());
            // 新增到数据库
            orderAndOthersMapper.insertDrugOrderDetail(drugOrderDetail);
        }
        clearRedisCache();
        return 1;
    }

    /**
     * 条件分页查询
     *
     * 修改方法的实现，增加缓存处理逻辑。流程如下：
     *      a. 前端发请求访问后端控制器
     *      b. 后端控制器，调用服务代码
     *      c. 服务代码，访问Redis，查询缓存中的采购入库单据集合。
     *      d. 如果Redis中有采购入库单据集合，则直接返回，不访问数据库查询数据。
     *      e. 如果Redis中没有采购入库单据集合，则访问数据库，查询采购入库单据集合。
     *      f. 把数据库中查询到的采购入库单据集合，保存到Redis中，并设置有效期为10分钟。
     *      g. 服务返回查询结果给控制器。 控制器返回查询结果给前端应用。
     *
     * @param orderAndOthers
     * @return
     */
    @Override
    public List<DrugsOrderAndOthers> selectList(DrugsOrderAndOthers orderAndOthers) {
        // 维护key对象。要考虑不同的查询条件和分页逻辑。
        // 基于若依项目提供的工具，获取请求中的分页条件数据
        PageDomain pageDomain = TableSupport.buildPageRequest();
        // 页码
        Integer pageNum = pageDomain.getPageNum();
        // 基于方法的参数，获取查询条件，供应商、申请人、状态
        String supportName = orderAndOthers.getSupportName();
        // 申请人
        String orderMan = orderAndOthers.getOrderMan();
        // 状态。 如果是状态是null，则设置status为 "-1"
        String status = orderAndOthers.getDetailStatus() == null ? "-1" : orderAndOthers.getDetailStatus().toString();
        if(pageNum == null){ // 默认是第一页
            pageNum = 1;
        }
        // redis中保存的key的格式是：  ruoyi:drugs:order:供应商:申请人:状态:页码
        String key = "ruoyi:drugs:order:" + supportName + ":" + orderMan + ":" + status + ":" + pageNum;

        // 访问Redis服务器， 查询缓存数据集合。
        List<DrugsOrderAndOthers> cache = redisDao.get(key);
        // 判断有没有缓存数据
        if(cache != null){
            // 有缓存，不再查询数据库，直接返回缓存数据
            return cache;
        }

        // 没有缓存，需要查询数据库
        List<DrugsOrderAndOthers> list = orderAndOthersMapper.selectList(orderAndOthers);

        // 把从数据库中查询到的采购入库单，保存的redis中。设置有效时长是10分钟
        redisDao.set(key, list, 10L, TimeUnit.MINUTES);
//        redisDao.set(key, list);
//        redisDao.expire(key, 10L, TimeUnit.MINUTES);

        // 把数据库查询结果返回给控制器。
        return list;
    }

    /**
     * 条件分页查询待审核状态的采购入库单
     * @param orderAndOthers
     * @return
     */
    @Override
    public List<DrugsOrderAndOthers> selectExamineStatusList(DrugsOrderAndOthers orderAndOthers) {
        // 设置状态数据。 只查询待审核状态的数据。
        orderAndOthers.setDetailStatus(1);
        return orderAndOthersMapper.selectList(orderAndOthers);
    }

    /**
     * 批量审核通过，状态变更为 审核成功（3）
     * @param detailIds
     * @return
     */
    @Override
    public int examineSuccess(Integer[] detailIds) {
        int rows = 0;
        for(Integer detailId : detailIds){
            // 创建要修改的对象
            DrugOrderDetail orderDetail = new DrugOrderDetail();
            orderDetail.setDetailId(detailId);
            orderDetail.setDetailStatus(3);
            // 更新数据库
            rows += orderAndOthersMapper.updateDrugOrderDetail(orderDetail);
        }
        clearRedisCache();
        return rows;
    }

    /**
     * 批量审核不通过，状态变更为 审核失败（2）
     * @param detailIds
     * @return
     */
    @Override
    public int examineCancel(Integer[] detailIds) {
        int rows = 0;
        for(Integer detailId : detailIds){
            // 创建对象
            DrugOrderDetail orderDetail = new DrugOrderDetail();
            orderDetail.setDetailId(detailId);
            orderDetail.setDetailStatus(2);
            // 更新
            rows += orderAndOthersMapper.updateDrugOrderDetail(orderDetail);
        }
        clearRedisCache();
        return rows;
    }
}
